/*
** Copyright (c) 2010  Kimmo 'Rainy' Pekkola
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program.  If not, see http://www.gnu.org/licenses.
*/

#include "timegroup.h"

//-----------------------------------------------------------------------------
/**
** Constructor.
**
** \param pParent The parent object.
*/
CTimeGroup::CTimeGroup(QObject* pParent) : QObject(pParent)
{
    m_pHours = NULL;
    m_pMinutes = NULL;
    m_pSeconds = NULL;
    m_pSeparator1 = NULL;
    m_pSeparator2 = NULL;

    m_bEditMode = false;
    m_bStopUpdates = false;
}

//-----------------------------------------------------------------------------
/**
** Destructor.
*/
CTimeGroup::~CTimeGroup()
{
    delete m_pHours;
    delete m_pMinutes;
    delete m_pSeconds;
    delete m_pSeparator1;
    delete m_pSeparator2;
}

//-----------------------------------------------------------------------------
/**
** Initializes the time group. Creates the items to the scene.
**
** \param pScene Pointer to the scene.
*/
void CTimeGroup::initialize(QGraphicsScene* pScene)
{
    m_pHours = new CRotator(this);
    m_pMinutes = new CRotator(this);
    m_pSeconds = new CRotator(this);

    m_pHours->buildScene(pScene, -NUMBER_ITEM_WIDTH - NUMBER_ITEM_WIDTH / 2 - SEPARATOR_WIDTH - (SEPARATOR_OFFSET * 2));
    m_pMinutes->buildScene(pScene, -NUMBER_ITEM_WIDTH / 2);
    m_pSeconds->buildScene(pScene, NUMBER_ITEM_WIDTH / 2 + SEPARATOR_WIDTH + (SEPARATOR_OFFSET * 2));

    bool bOk = false;
    bOk = connect(m_pHours, SIGNAL(valueChanged(int)), this, SLOT(onHoursChanged(int)));
    Q_ASSERT(bOk);
    bOk = connect(m_pMinutes, SIGNAL(valueChanged(int)), this, SLOT(onMinutesChanged(int)));
    Q_ASSERT(bOk);
    bOk = connect(m_pSeconds, SIGNAL(valueChanged(int)), this, SLOT(onSecondsChanged(int)));
    Q_ASSERT(bOk);

    // Add the separator colons
    m_pSeparator1 = new CNumberItem(NULL, -NUMBER_ITEM_WIDTH / 2 - SEPARATOR_WIDTH, -NUMBER_ITEM_HEIGHT / 2, true);
    m_pSeparator2 = new CNumberItem(NULL, NUMBER_ITEM_WIDTH / 2 + (2 * SEPARATOR_OFFSET), -NUMBER_ITEM_HEIGHT / 2, true);
    pScene->addItem(m_pSeparator1);
    pScene->addItem(m_pSeparator2);
}

//-----------------------------------------------------------------------------
/**
** Adds list of predefined values for the time group. If the listValues
** is provided the timegroup will contain only the given values (+selectedValue).
**
** \param listValues Optional list of values for the time group.
** \param selectedValue If one in listValues it is centered. Otherwise added to the end.
*/
void CTimeGroup::setFixedValues(const QList<int>& listValues, int selectedValue)
{
    if (listValues.isEmpty() && selectedValue == -1)
    {
        m_pHours->setFixedValues(listValues);
        m_pMinutes->setFixedValues(listValues);
        m_pSeconds->setFixedValues(listValues);

        disconnect(m_pHours, SIGNAL(offsetChanged(int)), m_pMinutes, SLOT(setOffset(int)));
        disconnect(m_pHours, SIGNAL(offsetChanged(int)), m_pSeconds, SLOT(setOffset(int)));
        disconnect(m_pMinutes, SIGNAL(offsetChanged(int)), m_pHours, SLOT(setOffset(int)));
        disconnect(m_pMinutes, SIGNAL(offsetChanged(int)), m_pSeconds, SLOT(setOffset(int)));
        disconnect(m_pSeconds, SIGNAL(offsetChanged(int)), m_pMinutes, SLOT(setOffset(int)));
        disconnect(m_pSeconds, SIGNAL(offsetChanged(int)), m_pHours, SLOT(setOffset(int)));

        disconnect(m_pHours, SIGNAL(holdIndex(int)), this, SLOT(onPresetSelected(int)));
        disconnect(m_pMinutes, SIGNAL(holdIndex(int)), this, SLOT(onPresetSelected(int)));
        disconnect(m_pSeconds, SIGNAL(holdIndex(int)), this, SLOT(onPresetSelected(int)));
    }
    else
    {
        QList<int> listMinutes, listHours, listSeconds;

        for (int i = 0; i < listValues.count(); i++)
        {
            listHours.append(listValues[i] / 60 / 60);
            listMinutes.append((listValues[i] / 60) % 60);
            listSeconds.append(listValues[i] % 60);
        }

        int pos = listValues.indexOf(selectedValue);
        if (pos == -1)
        {
            listHours.insert(0, selectedValue / 60 / 60);
            listMinutes.insert(0, (selectedValue / 60) % 60);
            listSeconds.insert(0, selectedValue % 60);
        }

        m_pHours->setFixedValues(listHours);
        m_pMinutes->setFixedValues(listMinutes);
        m_pSeconds->setFixedValues(listSeconds);

        if (pos != -1)
        {
            m_pHours->setOffset(pos * NUMBER_ITEM_HEIGHT);
            m_pMinutes->setOffset(pos * NUMBER_ITEM_HEIGHT);
            m_pSeconds->setOffset(pos * NUMBER_ITEM_HEIGHT);
        }
        else
        {
            m_pHours->setDimIndex(0, true);
            m_pMinutes->setDimIndex(0, true);
            m_pSeconds->setDimIndex(0, true);
        }

        bool bOk = false;
        bOk = connect(m_pHours, SIGNAL(offsetChanged(int)), m_pMinutes, SLOT(setOffset(int)));
        Q_ASSERT(bOk);
        bOk = connect(m_pHours, SIGNAL(offsetChanged(int)), m_pSeconds, SLOT(setOffset(int)));
        Q_ASSERT(bOk);
        bOk = connect(m_pMinutes, SIGNAL(offsetChanged(int)), m_pHours, SLOT(setOffset(int)));
        Q_ASSERT(bOk);
        bOk = connect(m_pMinutes, SIGNAL(offsetChanged(int)), m_pSeconds, SLOT(setOffset(int)));
        Q_ASSERT(bOk);
        bOk = connect(m_pSeconds, SIGNAL(offsetChanged(int)), m_pMinutes, SLOT(setOffset(int)));
        Q_ASSERT(bOk);
        bOk = connect(m_pSeconds, SIGNAL(offsetChanged(int)), m_pHours, SLOT(setOffset(int)));
        Q_ASSERT(bOk);

        bOk = connect(m_pHours, SIGNAL(holdIndex(int)), this, SLOT(onPresetSelected(int)));
        Q_ASSERT(bOk);
        bOk = connect(m_pMinutes, SIGNAL(holdIndex(int)), this, SLOT(onPresetSelected(int)));
        Q_ASSERT(bOk);
        bOk = connect(m_pSeconds, SIGNAL(holdIndex(int)), this, SLOT(onPresetSelected(int)));
        Q_ASSERT(bOk);
    }
}

QList<int> CTimeGroup::fixedValues()
{
    QList<int> listFixed;

    for (int i = 0; i < m_pHours->fixedValues().count(); i++)
    {
        if (m_pHours->dimmedIndexes().indexOf(i) == -1)
        {
            int hour = m_pHours->fixedValues()[i];
            int minute = m_pMinutes->fixedValues()[i];
            int second = m_pSeconds->fixedValues()[i];
            listFixed.append(hour * 60 * 60 + minute * 60 + second);
        }
    }
    return listFixed;
}

//-----------------------------------------------------------------------------
/**
** Sets a new time.
*/
void CTimeGroup::setValue(int value)
{
    if (m_pSeconds && m_pMinutes && m_pHours)
    {
        if (!m_bStopUpdates)
        {
            m_pSeconds->setValue(value % 60);
            m_pMinutes->setValue((value / 60) % 60);
            m_pHours->setValue((value / 60 / 60) % 60);
        }

        m_CurrentValue = value;
    }
}

//-----------------------------------------------------------------------------
/**
** Enables/disables the edit mode for the rotators.
**
** \param bEnable Set to true to go to enable mode. False to normal mode.
*/
void CTimeGroup::enableEditMode(bool bEnable)
{
    if (m_pSeconds && m_pMinutes && m_pHours)
    {
        m_bEditMode = bEnable;
        m_pSeconds->enableEditMode(bEnable);
        m_pMinutes->enableEditMode(bEnable);
        m_pHours->enableEditMode(bEnable);
    }
}

//-----------------------------------------------------------------------------
/**
** Sets the position for the time group in the scene.
**
** \param x The x position.
** \param y The y position.
*/
void CTimeGroup::setPosition(int x, int y)
{
    m_pHours->setPosition(x, y);
    m_pMinutes->setPosition(x, y);
    m_pSeconds->setPosition(x, y);
    m_pSeparator1->setPosition(x, y);
    m_pSeparator2->setPosition(x, y);
}

//-----------------------------------------------------------------------------
/**
** Enables the reflection all contained numbers.
**
** \param bEnable Set to true to enable, false to disable.
*/
void CTimeGroup::setReflection(bool bEnable)
{
    m_pSeconds->setReflection(bEnable);
    m_pMinutes->setReflection(bEnable);
    m_pHours->setReflection(bEnable);
    m_pSeparator1->setReflection(bEnable);
    m_pSeparator2->setReflection(bEnable);
}

//-----------------------------------------------------------------------------
/**
** Sets the transparency for the items.
**
** \param opacity The new opacity value.
*/
void CTimeGroup::setOpacity(qreal opacity)
{
    m_pSeconds->setOpacity(opacity);
    m_pMinutes->setOpacity(opacity);
    m_pHours->setOpacity(opacity);
    m_pSeparator1->setOpacity(opacity);
    m_pSeparator2->setOpacity(opacity);
}

//-----------------------------------------------------------------------------
/**
** Sets the visibility for the items.
**
** \param bVisible True to show, False to hide.
*/
void CTimeGroup::setVisible(bool bVisible)
{
    m_pSeconds->setVisible(bVisible);
    m_pMinutes->setVisible(bVisible);
    m_pHours->setVisible(bVisible);
    m_pSeparator1->setVisible(bVisible);
    m_pSeparator2->setVisible(bVisible);
}

void CTimeGroup::stopMovement()
{
    m_pSeconds->stopMovement();
    m_pMinutes->stopMovement();
    m_pHours->stopMovement();
}

///////////////////////////////////////////////////////////////////////////////
/// SLOTS
///////////////////////////////////////////////////////////////////////////////

//-----------------------------------------------------------------------------
/**
** Slot which gets called when the hour value was changed. Updates the current
** value accordingly.
**
** \param value The new value.
*/
void CTimeGroup::onHoursChanged(int value)
{
    m_CurrentValue = (value % 60) * 60 * 60 + m_CurrentValue % (60 * 60);
    emit valueChanged(m_CurrentValue);
}

//-----------------------------------------------------------------------------
/**
** Slot which gets called when the minute value was changed. Updates the current
** value accordingly.
**
** \param value The new value.
*/
void CTimeGroup::onMinutesChanged(int value)
{
    m_CurrentValue = (value % 60) * 60 + m_CurrentValue % 60 + (m_CurrentValue / (60 * 60)) * (60 * 60);
    emit valueChanged(m_CurrentValue);
}

//-----------------------------------------------------------------------------
/**
** Slot which gets called when the second value was changed. Updates the current
** value accordingly.
**
** \param value The new value.
*/
void CTimeGroup::onSecondsChanged(int value)
{
    m_CurrentValue = (value % 60) + (m_CurrentValue / 60) * 60;
    emit valueChanged(m_CurrentValue);
}

//-----------------------------------------------------------------------------
/**
** Slot which gets called when a preset is selecter
**
** \param index The selected preset.
*/
void CTimeGroup::onPresetSelected(int index)
{
    // Check if the item is already dimmed or not
    int pos = m_pHours->dimmedIndexes().indexOf(index);
    m_pHours->setDimIndex(index, pos == -1);
    m_pMinutes->setDimIndex(index, pos == -1);
    m_pSeconds->setDimIndex(index, pos == -1);

    emit presetChanged();
}

// EOF
